/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Grandulon;
import java.util.*;
import java.io.*;
import java.net.*;
/**
 *
 * @author MariaDeFreitas
 */
public class RunningThread implements Runnable{
    private Process process;
    private int total_processes;
    ServerSocket[] sock;
    Random r;

    public Process getProcess() {
        return process;
    }

    public void setProcess(Process process) {
        this.process = process;
    }

    public RunningThread(Process process, int total_processes) {
        this.process = process;
        this.total_processes = total_processes;
        this.r = new Random();
        this.sock = new ServerSocket[total_processes];
    }

    synchronized private void recovery() {
        System.out.println("Process[" + this.process.getPid() + "]: -> Recovered from Crash");
        //Find current co-ordinator.
    }

    synchronized private void pingCoOrdinator() {
        try {
            if (Election.isPingFlag()) {
                synchronized (Election.lock) {
                    Election.lock.wait();
                }
            }
            if (!Election.isElectionFlag()) {
                Election.setPingFlag(true);
                System.out.println("Process[" + this.process.getPid() + "]: Are you alive?");
                Socket outgoing = new Socket(InetAddress.getLocalHost(), 12345);
                outgoing.close();
                Election.setPingFlag(false);
                synchronized (Election.lock) {
                    Election.lock.notifyAll();
                }
            }
        } catch (Exception ex) {
            //Initiate Election
            System.out.println("process[" + this.process.getPid() + "]: -> Co-Ordinator is down\nInitiating Election");
            Election.setElectionFlag(true);
            Election.setPingFlag(false);
            synchronized (Election.lock) {
                Election.lock.notifyAll();
            }
        }
    }

    synchronized private void executeJob() {
        int temp = r.nextInt(20);
        for (int i = 0; i <= temp; i++) {
            try {
                Thread.sleep(700);
            } catch (InterruptedException e) {
                System.out.println("Error Executing Thread:" + process.getPid());
                System.out.println(e.getMessage());
            }
        }
    }

    synchronized private boolean sendMessage() {
        boolean response = false;
        int i = 0;
        try {
            if (Election.isMessageFlag()) {
                synchronized (Election.lock) {
                    Election.lock.wait();
                }
            }
            Election.setMessageFlag(true);
            if (Election.isElectionFlag()) {
                for (i = this.process.getPid() + 1; i <= this.total_processes; i++) {
                    try {
                        Socket electionMessage = new Socket(InetAddress.getLocalHost(), 10000 + i);
                        System.out.println("Process[" + this.process.getPid() + "] -> Process[" + i + "]  responded to election message successfully");
                        electionMessage.close();
                        response = true;
                    } catch (Exception ex) {
                        System.out.println("Process[" + this.process.getPid() + "] -> Process[" + i + "] did not respond to election message");
                    }
                }
            }
            Election.setMessageFlag(false);
            synchronized (Election.lock) {
                Election.lock.notifyAll();
            }
        } catch (Exception ex1) {
            System.out.println(ex1.getMessage());
        }

        return response;
    }

    synchronized private void serve() {
        try {
            //service counter
            Socket incoming = null;
            ServerSocket s = new ServerSocket(12345);
            for (int counter = 0; counter < 10; counter++) {
                incoming = s.accept();
                System.out.println("Process[" + this.process.getPid() + "]:Yes");
                Scanner scan = new Scanner(incoming.getInputStream());
                PrintWriter out = new PrintWriter(incoming.getOutputStream(), true);
                if (scan.hasNextLine()) {
                    if (scan.nextLine().equals("Who is the co-ordinator?")) {
                        System.out.print("Process[" + this.process.getPid() + "]:");
                        out.println(this.process);
                    }
                }
            }
            //after serving 10 requests go down for random time
            this.process.setCoOrdinatorFlag(false);
            this.process.setDownflag(true);
            try {
                incoming.close();
                s.close();
                sock[this.process.getPid() - 1].close();
                Thread.sleep((this.r.nextInt(10) + 1) * 1000000);//going down
                recovery();
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }


        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
    }

    @Override
    public void run() {
        try {
            sock[this.process.getPid() - 1] = new ServerSocket(10000 + this.process.getPid());
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
        while (true) {
            if (process.isCoOrdinatorFlag()) {
                //serve other processes
                serve();
            } else {
                while (true) {
                    //Execute some task
                    executeJob();

                    //Ping the co-ordinator
                    pingCoOrdinator();

                    if (Election.isElectionFlag()) {
                        if (!sendMessage()) {//elect self as co-ordinator
                            System.out.println("New Co-Ordinator: Process[" + this.process.getPid() + "]");
                            this.process.setCoOrdinatorFlag(true);
                            Election.setElectionFlag(false);
                            break;
                        }
                    }
                }
            }
        }
    }
}
